package egar

import "math"

type GameMap struct {
	game          *Game
	width, height float64
	balls         []*Ball
	hatchBalls    []*Ball
}

func newGameMap(game *Game) *GameMap {
	return &GameMap{
		game:       game,
		width:      game.config.GameMapWidth,
		height:     game.config.GameMapHeight,
		balls:      make([]*Ball, 0),
		hatchBalls: make([]*Ball, 0),
	}
}

func (m *GameMap) hatchBall(ball *Ball) {
	m.hatchBalls = append(m.hatchBalls, ball)
}

func (m *GameMap) mergeHatchBall() {
	for _, ball := range m.hatchBalls {
		m.balls = append(m.balls, ball)
		if ball.Type == TypeCell && ball.OwnerPlayer != nil {
			ball.OwnerPlayer.Cells.Add(ball)
			ball.OwnerPlayer.AliveCellTotal++
		}
	}
	m.hatchBalls = make([]*Ball, 0)
}

func (m *GameMap) cleanRemovedBall() {
	temp := make([]*Ball, 0)
	for _, ball := range m.balls {
		if ball.Removed {
			if ball.Type == TypeCell && ball.OwnerPlayer != nil {
				ball.OwnerPlayer.Cells.Remove(ball)
				ball.OwnerPlayer.AliveCellTotal--
			}
		} else {
			temp = append(temp, ball)
		}
	}
	m.balls = temp
}

func (m *GameMap) update(currentTime int64, deltaTime float64) {
	foodCount := 0
	virusCount := 0

	for _, ball := range m.balls {
		if ball.Removed {
			continue
		}
		switch ball.Type {
		case TypeFood:
			foodCount++
		case TypeVirus:
			virusCount++
		case TypeSpore:
			updateSpore(ball, deltaTime)
		case TypeCell:
			if ball.OwnerPlayer != nil && ball.OwnerPlayer.State == PlayerStatePlaying {
				updateCell(ball, deltaTime)
			}
			for _, target := range m.balls {
				if target.Removed {
					continue
				}
				// resolve collision
				if ball != target &&
					target.Type == TypeCell &&
					target.OwnerPlayer == ball.OwnerPlayer {
					resolveCellCollision(ball, target, deltaTime)
				}
				// eats
				if ball != target &&
					ball.OwnerPlayer != target.OwnerPlayer &&
					ball.Mass > target.Mass*m.game.config.OtherMinMassMultipleToEat &&
					ball.GetDistance(target.X, target.Y) < ball.Radius-target.Radius*m.game.config.OtherMinOverlapToEat {
					target.Removed = true
					ball.AddMass(target.Mass)
					if target.Type == TypeCell && target.OwnerPlayer != nil && target.OwnerPlayer.AliveCellTotal == 1 {
						target.OwnerPlayer.State = PlayerStateDead
					}
					if target.Type == TypeVirus {
						angle := 0.0
						splitCount := 0
						for i := 0; i < 9; i++ {
							if ball.Mass < m.game.config.CellMinMassToSplit || ball.OwnerPlayer.Cells.Len()+splitCount >= m.game.config.PlayerMaxCells {
								break
							}
							splitCount++
							mx := math.Cos(angle)
							my := math.Sin(angle)
							newCell := NewCellBall(
								ball.X+mx*ball.Radius,
								ball.Y+my*ball.Radius,
								ball.Color,
								m.game.config.CellMinMassToSplit,
								ball.OwnerPlayer,
							)
							newCell.setBoost(mx*64, my*64)
							m.hatchBall(newCell)
							ball.AddMass(-m.game.config.CellMinMassToSplit)
							angle += 40
						}
					}
				}
			}
		}
		if m.game.config.OtherBoundaryCollision {
			if ball.X < 0 {
				ball.X = 0
			}
			if ball.X > m.game.config.GameMapWidth {
				ball.X = m.game.config.GameMapWidth
			}
			if ball.Y < 0 {
				ball.Y = 0
			}
			if ball.Y > m.game.config.GameMapHeight {
				ball.Y = m.game.config.GameMapHeight
			}
		}
	}

	for i := foodCount; i < m.game.config.FoodTotal; i++ {
		m.hatchBall(NewFoodBall(m.game.config.FoodMinMass, m.game.config.FoodMaxMass))
	}

	for i := virusCount; i < m.game.config.VirusTotal; i++ {
		m.hatchBall(NewVirusBall(m.game.config.VirusMinMass, m.game.config.VirusMaxMass))
	}

	m.cleanRemovedBall()
	m.mergeHatchBall()
}
